package gwt.trading.client;

import gwt.trading.client.geometry.Point;
import gwt.trading.client.util.Closure;
import gwt.trading.client.util.ColorEnum;
import gwt.trading.client.util.GwtList;

import com.google.gwt.user.client.ui.Composite;

/**
 * @author Iulian Costan (iulian.costan@gmail.com)
 * 
 */
public class TradingChart extends Composite {

	private TradingChartSettings settings;
	private RendererImpl renderer;

	private GwtList /* <Area> */areas;
	private int xOffset;
	private int yOffset;

	public TradingChart(TradingChartSettings settings) {
		this.settings = settings;

		areas = new GwtList(10);
		renderer = new RendererImpl(settings.getRealWidth(), settings.getRealHeight());
		xOffset = settings.getPadding().getLeft();
		yOffset = settings.getPadding().getTop();

		initWidget(renderer);
	}

	public void add(DrawingArea area) {
		areas.add(area);
	}

	public void draw() {
		// clear chart before draw, try to avoid flickering
		clearChart();
		// draw each area
		drawAreas();
		// draw chart borders
		drawBorders();
	}

	public DrawingArea addDrawingArea(VerticalConstrainter masterAttachable, DrawingAreaSettings areaSettings) {

		// TODO compute heights automatically
		if (areaSettings.getHeight() > settings.getHeight() - yOffset) {
			areaSettings.adjustHeight(settings.getHeight() - yOffset);
		}

		DrawingArea drawingArea = new DrawingArea(masterAttachable, settings, areaSettings, new Point(xOffset, yOffset));
		add(drawingArea);

		// move down vertical offset
		yOffset = yOffset + areaSettings.getHeight() + settings.getSpacing().getVerticalSpacing();

		return drawingArea;
	}

	private void drawAreas() {
		areas.forAll(new Closure() {
			public void execute(Object object) {
				DrawingArea area = (DrawingArea) object;
				area.draw(renderer);
			}
		});
	}

	private void clearChart() {
		renderer.clear(0, 0, settings.getRealWidth(), settings.getRealHeight());
	}

	private void drawBorders() {
		int width = settings.getRealWidth();
		int height = settings.getRealHeight();
		ColorEnum borderColor = settings.getBorderColor();

		// top
		renderer.drawLine(new Line(new Point(1, 1), new Point(width, 1), borderColor));
		// right
		renderer.drawLine(new Line(new Point(width - 1, 1), new Point(width - 1, height), borderColor));
		// bottom
		renderer.drawLine(new Line(new Point(width, height - 1), new Point(1, height - 1), borderColor));
		// left
		renderer.drawLine(new Line(new Point(1, 1), new Point(1, height), borderColor));
	}

	public Attachable getAttachable(final String id) {
		FindClosure closure = new FindClosure(id);
		areas.forAll(closure);
		return closure.getAttachable();
	}
	
	public Attachable remove(String id) {
		DeleteClosure closure = new DeleteClosure(id);
		areas.forAll(closure);
		return closure.getAttachable();
	}

	private class FindClosure implements Closure {

		private String id;
		private Attachable attachable;

		public FindClosure(String id) {
			this.id = id;
		}

		public void execute(Object object) {
			DrawingArea area = (DrawingArea) object;
			Attachable attachable = area.getAttachable(id);
			if (attachable != null) {
				this.attachable = attachable;
			}
		}

		public Attachable getAttachable() {
			return attachable;
		}
	}
	
	private class DeleteClosure implements Closure {
		
		private String id;
		private Attachable attachable;
		
		public DeleteClosure(String id) {
			this.id = id;
		}
		
		public void execute(Object object) {
			DrawingArea area = (DrawingArea) object;
			Attachable attachable = area.remove(id);
			if (attachable != null) {
				this.attachable = attachable;
			}
		}
		
		public Attachable getAttachable() {
			return attachable;
		}
	}

}
